using System;
using System.Collections;
namespace fuzzyKittenVNC
{
	public class VNCClientFramebuffer
	{
		public struct Region {
			public uint x;
			public uint y;
			public uint w;
			public uint h;
		}
		
		public VNCMessages.ServerInit format;
		public byte[] pixel;
		public ArrayList dirtyRegions = new ArrayList();
		public VNCClient client;
		private static ArrayList clients;
		private VNCClientFramebuffer(VNCClient client, VNCMessages.ServerInit initMsg)
		{
			this.client = client;
			this.updateDimensions(initMsg);
		}
	
		/**
		 * Returns a framebuffer for a client if it exists, otherwise creates a new and returns it
		 */
		public static VNCClientFramebuffer getInstanceForClient(VNCClient client, VNCMessages.ServerInit initMsg) {
			VNCClientFramebuffer fb;
			fb = VNCClientFramebuffer.getInstanceForClient(client);
			if(fb == null) {
				fb = new VNCClientFramebuffer(client,initMsg);
				clients.Add(fb);
			}
			return fb;
		}
		
		/**
		 * Returns a framebuffer for a client if it exists, otherwise returns null
		 */
		public static VNCClientFramebuffer getInstanceForClient(VNCClient client) {
			if(clients == null)
				clients = new ArrayList();
			for(int i=0;i<clients.Count;i++) {
				// free old references for the garbage collector
				if(((VNCClientFramebuffer) clients[i]).client == null) {
					clients[i] = null; 
					continue;
				}
				if(((VNCClientFramebuffer) clients[i]).client == client)
					return (VNCClientFramebuffer) clients[i];
			}
			return null;
		}
		
		public void updateDimensions(VNCMessages.ServerInit initMsg) {
			int width,height,bpp;
			width = initMsg.frameBufferWidth;
			height = initMsg.frameBufferHeight;
			bpp = initMsg.pixFormat.bits_per_pixel;
			pixel = new byte[width*height*bpp/8];

			this.format = initMsg;
		}		
		
		public void updateArea(uint x, uint y, uint w, uint h, byte[] newPixel) {
			uint bpp = (uint) this.format.pixFormat.bits_per_pixel;
			
			uint stride = (uint) this.format.frameBufferWidth*bpp/8;
			uint offsetX = x*bpp/8;

			uint offsetY = stride*y;
			
			for(int i=3;i<newPixel.Length;i+=4)
				newPixel[i] = 0xff;	
			
			Region region;
			region.h = h;
			region.w = w;
			region.x = x;
			region.y = y;
			dirtyRegions.Add(region);
			for(int i=0;i<h;i++) {
				Array.Copy(newPixel, w*i*bpp/8,
				           this.pixel, offsetX+offsetY+stride*i,
				           w*bpp/8);
			}

		}
		
		public byte[] getArea(uint x, uint y, uint w, uint h) {
			uint bpp = (uint) this.format.pixFormat.bits_per_pixel;
			byte[] returnArea = new byte[w*h*bpp/8];
			uint stride = (uint) this.format.frameBufferWidth*bpp/8;
			uint offsetX = x*bpp/8;

			uint offsetY = stride*y;
			
			for(int i=0;i<h;i++) {
				Array.Copy(this.pixel, offsetX+offsetY+stride*i,
				           returnArea, w*i*bpp/8,
				           w*bpp/8);
			}
			return returnArea;
		}
	}
}

